package com.acme.logginglab.appenders;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import com.acme.logginglab.mylogger.LogLevel;

public class AsyncFileAppender implements Appender {
	private final String propertyFileName = "/mylogger.properties";
	private final BlockingQueue<String> msgBuffer;
	private static AsyncFileAppender instance = null;
	private PrintWriter logFileWriter;
	private String format;
	
	public static AsyncFileAppender getInstance() {
		if (instance == null) {
			instance = new AsyncFileAppender();
		}
		return instance;
	}

	private AsyncFileAppender() {
		InputStream in = getClass().getResourceAsStream(propertyFileName);
		Properties props = new Properties();
		String logFileName;
		int bufferSize = 0;
		try {
			props.load(in);
			logFileName = props.getProperty("mylogger.appender.filename");
			format = props.getProperty("mylogger.appender.format");
			if (format == null) {
				format = "DEFAULT %d %l %m [%t]";
			}
			try {
				bufferSize = Integer.parseInt(props.getProperty("mylogger.appender.buffer"));
			} catch (NumberFormatException e) {
				bufferSize = 128;
			}

			if (logFileName == null) {
				logFileWriter = new PrintWriter(System.out, true);
			} else {
				logFileWriter = new PrintWriter(new BufferedWriter(new FileWriter(logFileName, true)), true);
			}
		} catch (IOException e) {
			logFileWriter = new PrintWriter(System.out, true);
		}
		msgBuffer = new LinkedBlockingQueue<String>(bufferSize);
		getFileWriter().start();
	}

	public synchronized void append(LogLevel l, String msg) {
		try {
			String formattedMsg = format.replace("%m", msg)
										.replace("%n", "\n")
										.replace("%d", new Date().toString())
										.replace("%l", l.toString())
										.replace("%t", Thread.currentThread().getName())
										;
			
			msgBuffer.put(formattedMsg);
		} catch (IllegalStateException e) {
			System.err.println("No space in buffer");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 
	}

	private Thread getFileWriter() {
		Thread thread = new Thread() {
			@Override
			public void run() {
				try {
					while (true) {
						String msg = msgBuffer.take();
						logFileWriter.print(msg);
						logFileWriter.flush();
					}
				} catch (InterruptedException e) {
					Thread.currentThread().interrupt();
				} finally {
					logFileWriter.close();
				}
			}
		};
		thread.setDaemon(true);
		return thread;
	}
}
